package com.lj.tools.util;

import com.lj.tools.exception.YerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.List;
import java.util.Locale;


public class Md5Util {
    static Logger log = LoggerFactory.getLogger(Md5Util.class);

    public static String join(List<?> items, String delimiter, boolean needTrim) {
        StringBuilder sb = new StringBuilder();

        for(int i = 0; i < items.size(); ++i) {
            String item = items.get(i).toString();
            sb.append(needTrim ? item.trim() : item);
            if (i < items.size() - 1) {
                sb.append(delimiter);
            }
        }

        return sb.toString();
    }

    public static String join(List<?> items, String delimiter) {
        return join(items, delimiter, false);
    }


    public static String toHex(byte[] data) {
        if (null == data) {
            return null;
        } else if (data.length <= 0) {
            return "";
        } else {
            StringBuilder sb = new StringBuilder();
            int dataLength = data.length;

            for(int index = 0; index < dataLength; ++index) {
                byte datum = data[index];
                String hv = Integer.toHexString(datum);
                if (hv.length() < 2) {
                    sb.append("0");
                } else if (hv.length() == 8) {
                    hv = hv.substring(6);
                }

                sb.append(hv);
            }

            return sb.toString().toLowerCase(Locale.getDefault());
        }
    }

    public static byte[] fromHex(String hexData) {
        if (null == hexData) {
            return new byte[0];
        } else if ((hexData.length() & 1) == 0 && hexData.replaceAll("[a-fA-F0-9]", "").length() <= 0) {
            byte[] result = new byte[(hexData.length() + 1) / 2];
            int offset = 0;

            String hexNumber;
            for(int index = 0; offset < hexData.length(); result[index++] = (byte)Integer.parseInt(hexNumber, 16)) {
                hexNumber = hexData.substring(offset, offset + 2);
                offset += 2;
            }

            return result;
        } else {
            throw new IllegalArgumentException("'" + hexData + "' is not a hex string");
        }
    }

    public static String toBase64(byte[] data) {
        return new String(Base64.getEncoder().encode(data), StandardCharsets.UTF_8).replaceAll("\\s", "");
    }


    public static byte[] fromBase64(String b64Data) {
        return Base64.getDecoder().decode(b64Data.getBytes(StandardCharsets.UTF_8));
    }



    public static byte[] computeMD5Hash(InputStream is) throws NoSuchAlgorithmException, IOException {
        BufferedInputStream bis = null;

        try {
            bis = new BufferedInputStream(is);
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[16384];

            int bytesRead;
            while((bytesRead = bis.read(buffer, 0, buffer.length)) != -1) {
                messageDigest.update(buffer, 0, bytesRead);
            }

            return messageDigest.digest();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException var12) {
                    if (log.isWarnEnabled()) {
                        log.warn("close failed.", var12);
                    }
                }
            }

        }
    }

    public static byte[] computeMD5Hash(InputStream is, long length, long offset) throws NoSuchAlgorithmException, IOException {
        BufferedInputStream bis = null;

        try {
            bis = new BufferedInputStream(is);
            if (offset > 0L) {
                long skipByte = bis.skip(offset);
                if (log.isDebugEnabled()) {
                    log.debug("computeMD5Hash: Skip " + skipByte + " bytes");
                }
            }

            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[16384];
            int bytesRead = -1;
            long readLen = 0L;

            for(long bufLen = 16384L > length ? length : 16384L; readLen < length && (bytesRead = bis.read(buffer, 0, (int)bufLen)) != -1; bufLen = length - readLen > 16384L ? 16384L : length - readLen) {
                messageDigest.update(buffer, 0, bytesRead);
                readLen += (long)bytesRead;
            }

            return messageDigest.digest();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    if (log.isWarnEnabled()) {
                        log.warn("close failed.", e);
                    }
                }
            }

        }
    }

    public static String computeMD5(String data) throws YerException {
        try {
            return toBase64(computeMD5Hash(data.getBytes(StandardCharsets.UTF_8)));
        } catch (IOException | NoSuchAlgorithmException e) {
            throw new YerException("Failed to get MD5 for requestXmlElement:" + data);
        }
    }

    public static byte[] computeMD5Hash(byte[] data) throws NoSuchAlgorithmException, IOException {
        return computeMD5Hash((InputStream)(new ByteArrayInputStream(data)));
    }
}
